#!/bin/sh
# SPDX-License-Identifier: GPL-2.0
# Copyright (C) 2023 JELOS (https://github.com/JustEnoughLinuxOS)

# A simple tool to manipulate the controller LEDs using outb, thanks
# in large part to Maya Matuszczyk (https://github.com/Maccraft123)
# for help with reverse engineering.

. /etc/profile

OUTB="/usr/bin/outb"

ADDR_PORT="0x4e"
DATA_PORT="0x4f"

DEBUG=false

function debug_out() {
  $DEBUG && echo "ledcontrol: $*"
}

function led_ctl() {
  ${OUTB} ${ADDR_PORT} 0x2e
  ${OUTB} ${DATA_PORT} 0x11
  ${OUTB} ${ADDR_PORT} 0x2f
  ${OUTB} ${DATA_PORT} 0xd1 # high

  ${OUTB} ${ADDR_PORT} 0x2e
  ${OUTB} ${DATA_PORT} 0x10
  ${OUTB} ${ADDR_PORT} 0x2f
  ${OUTB} ${DATA_PORT} ${1} # low

  ${OUTB} ${ADDR_PORT} 0x2e
  ${OUTB} ${DATA_PORT} 0x12
  ${OUTB} ${ADDR_PORT} 0x2f
  ${OUTB} ${DATA_PORT} ${2} # data
}

function led_open() {
  led_ctl 0x87 0xa5
}

function led_close() {
  led_ctl ${1} 0x01
}

function led_ack() {
  led_open
  led_ctl 0x70 0x0
  led_close 0x86
}

function led_state() {
  # 0x31 = off
  # 0x37 = on
  for ZONE in 0xb2 0x72
  do
    led_open
    led_ctl ${ZONE} ${1}
    led_ctl 0xc6 0x01
    led_ack
  done
}

function led_enable() {
  led_state 0x37
  led_ack

  led_open
    led_ctl 0xbf 0x0
  led_close 0xc6

  led_open
    led_ctl 0x7f 0x0
  led_close 0xc6

  led_ack

  led_open
    led_ctl 0xc0 0x0
  led_close 0xc6

  led_open
    led_ctl 0x80 0x0
  led_close 0x86

  led_ack

  led_open
    led_ctl 0xc1 0x5
  led_close 0xc6

  led_open
    led_ctl 0x81 0x5
  led_close 0xc6

  led_ack

  led_open
    led_ctl 0xc2 0x5
  led_close 0xc6

  led_open
    led_ctl 0x82 0x5
  led_close 0x86

  led_open
    led_ctl 0x70 0x0
  led_close 0x86

  led_open
    led_ctl 0xc3 0x5
  led_close 0x86

  led_open
    led_ctl 0x83 0x5
  led_close 0x86

  led_ack

  led_open
    led_ctl 0xc4 0x5
  led_close 0xc6

  led_open
    led_ctl 0x84 0x5
  led_close 0x86

  led_ack

  led_open
    led_ctl 0xc5 0x7
  led_close 0xc6

  led_open
    led_ctl 0x85 0x7
  led_close 0x86

  led_open
    led_ctl 0x70 0x0
  led_close 0x86

  led_open
    led_ctl 0xc5 0x7
  led_close 0xc6

  led_open
    led_ctl 0x85 0x7
  led_close 0x86

  led_open
    led_ctl 0x70 0x0
  led_close 0x86

  led_open
    led_ctl 0xb2 0xba
  led_close 0xc6

  led_open
    led_ctl 0x72 0xba
  led_close 0x86

  led_open
    led_ctl 0x70 0x0
  led_close 0x86

  led_ack

}

function led_color() {
  # Zone 1 (Left Stick)
  led_open
    led_ctl 0xB3 ${1} # Quadrant 1
  led_ack

  led_open
    led_ctl 0xB4 ${2}
  led_ack

  led_open
    led_ctl 0xB5 ${3}
  led_ack

  led_open
    led_ctl 0xB6 ${1} # Quadrant 2
  led_ack

  led_open
    led_ctl 0xB7 ${2}
  led_ack

  led_open
    led_ctl 0xB8 ${3}
  led_ack

  led_open
    led_ctl 0xB9 ${1} # Quadrant 3
  led_ack

  led_open
    led_ctl 0xBA ${2}
  led_ack

  led_open
    led_ctl 0xBB ${3}
  led_ack

  led_open
    led_ctl 0xBC ${1} # Quadrant 4
  led_ack

  led_open
    led_ctl 0xBD ${2}
  led_ack

  led_open
    led_ctl 0xBE ${3}
  led_ack

  # Zone 2 (Right Stick)
  led_open
    led_ctl 0x73 ${1} # Quadrant 1
  led_ack

  led_open
    led_ctl 0x74 ${2}
  led_ack

  led_open
    led_ctl 0x75 ${3}
  led_ack

  led_open
    led_ctl 0x76 ${1} # Quadrant 2
  led_ack

  led_open
    led_ctl 0x77 ${2}
  led_ack

  led_open
    led_ctl 0x78 ${3}
  led_ack

  led_open
    led_ctl 0x79 ${1} # Quadrant 3
  led_ack

  led_open
    led_ctl 0x7A ${2}
  led_ack

  led_open
    led_ctl 0x7B ${3}
  led_ack

  led_open
    led_ctl 0x7C ${1} # Quadrant 4
  led_ack

  led_open
    led_ctl 0x7D ${2}
  led_ack

  led_open
    led_ctl 0x7E ${3}
  led_ack
}

function intensity() {
  printf "0x%X\n" $((${1} / ${2}))
}

GETBRIGHTNESS=$(get_setting led.brightness)
if [ ! -z "${2}" ]
then
  LEDBRIGHTNESS=${2}
  debug_out "Arg[2]: ${2}"
elif [ ! -z "${GETBRIGHTNESS}" ]
then
  LEDBRIGHTNESS=${GETBRIGHTNESS}
  debug_out "GETBRIGHTESS: ${GETBRIGHTNESS}"
else
  debug_out "NO SETTING: min"
  LEDBRIGHTNESS=min
  set_setting led.brightness min
fi

case ${LEDBRIGHTNESS} in
  max)
    LEDBRIGHTNESS=1
    set_setting led.brightness max
  ;;
  mid)
    LEDBRIGHTNESS=4
    set_setting led.brightness mid
  ;;
  min)
    LEDBRIGHTNESS=8
    set_setting led.brightness min
  ;;
esac

case ${1} in
  poweroff)
    led_state 0x31
  ;;
  off)
    led_state 0x31
    set_setting led.color off
  ;;
  on)
    led_state 0x37
    set_setting led.color on
    led_enable
  ;;
  red)
    COLOR=$(intensity 0xFF ${LEDBRIGHTNESS})
    ledcontrol off
    led_color ${COLOR} 0x00 0x00
    set_setting led.color red
    led_enable
  ;;
  green)
    COLOR=$(intensity 0xFF ${LEDBRIGHTNESS})
    ledcontrol off
    led_color 0x00 ${COLOR} 0x00
    set_setting led.color green
    led_enable
  ;;
  blue)
    COLOR=$(intensity 0xFF ${LEDBRIGHTNESS})
    ledcontrol off
    led_color 0x00 0x00 ${COLOR}
    set_setting led.color blue
    led_enable
  ;;
  teal)
    COLOR=$(intensity 0xFF ${LEDBRIGHTNESS})
    ledcontrol off
    led_color 0x00 ${COLOR} ${COLOR}
    set_setting led.color teal
    led_enable
  ;;
  purple)
    COLOR=$(intensity 0xFF ${LEDBRIGHTNESS})
    ledcontrol off
    led_color ${COLOR} 0x00 ${COLOR}
    set_setting led.color purple
    led_enable
  ;;
  white)
    COLOR=$(intensity 0xFF ${LEDBRIGHTNESS})
    ledcontrol off
    led_color ${COLOR} ${COLOR} ${COLOR}
    set_setting led.color white
    led_enable
  ;;
  default)
    del_setting led.color
  ;;
  brightness)
    set_setting led.brightness ${2}
    ledcontrol $(get_setting led.color)
  ;;
  list)
    cat <<EOF
default
off
red
green
blue
teal
purple
white
EOF
  ;;
  *)
    COLOR=$(get_setting led.color)
    if [ ! -z "${COLOR}" ]
    then
      ledcontrol ${COLOR}
    fi
  ;;
esac
